Imports System.Runtime.InteropServices

Namespace ClientSock

    Friend Module mSocket

        Public gIsConnecting As Boolean

        Public WithEvents gSock As ClientSocket

        Private bIncompleteData() As Byte = Nothing

        Public Sub ConnectTo(ByRef ip As System.Net.IPAddress, ByRef port As Int16)

            gIsConnecting = True

            If gSock Is Nothing Then
                gSock = New ClientSocket()
            Else
                gSock.Disconnect()
                gSock = New ClientSocket()
            End If

            gSock.Connect(ip, port)

        End Sub

        Public Sub ConnectTo(ByRef host As String, ByRef port As Int16)

            gIsConnecting = True

            If gSock Is Nothing Then
                gSock = New ClientSocket()
            Else
                gSock.Disconnect()
                gSock = New ClientSocket()
            End If

            gSock.Connect(host, port)

        End Sub

        Public Sub Poll()

            gSock.Poll()

        End Sub

        Public Sub Shutdown()

            gSock.Disconnect()
            gSock = Nothing

        End Sub

        Public Sub SendData(ByRef bytes() As Byte)

            gSock.SendData(bytes)

        End Sub

        Private Sub gSock_onConnect() Handles gSock.onConnect

            gIsConnecting = False
            MessageBoxes.RemoveStateNotice()

        End Sub

        Private Sub gSock_onDataRecieved(ByRef data() As Byte, ByRef bytes As Integer) Handles gSock.onDataRecieved

            Dim bRecievedBytes() As Byte

            If Not bIncompleteData Is Nothing Then
                If bytes > 0 Then
                    ReDim bRecievedBytes(bytes + bIncompleteData.Length - 1)
                    Array.Copy(bIncompleteData, 0, bRecievedBytes, 0, bIncompleteData.Length)
                    Array.Copy(data, 0, bRecievedBytes, bIncompleteData.Length, bytes)
                Else
                    ReDim bRecievedBytes(bIncompleteData.Length - 1)
                    Array.Copy(bIncompleteData, 0, bRecievedBytes, 0, bIncompleteData.Length)
                End If
                bIncompleteData = Nothing
            Else
                ReDim bRecievedBytes(bytes - 1)
                Array.Copy(data, bRecievedBytes, bytes)
            End If

            Dim ret As EvoLib.Protocol.ParsePacketCode

            Dim info As New EvoLib.Protocol.Packet
            Dim struct() As Byte = Nothing

            Try
                If Globals.Crypto Is Nothing Then
                    ret = EvoLib.Protocol.ParsePacket(bRecievedBytes, info, struct)
                Else
                    ret = EvoLib.Protocol.ParsePacket(bRecievedBytes, info, struct, Globals.Crypto)
                End If
            Catch Ex As System.Security.Cryptography.CryptographicException
                ret = ParsePacketCode.Incomplete
            Catch Ex As Exception
                If Not bRecievedBytes Is Nothing Then
                    If bRecievedBytes.Length > 0 Then
                        ret = ParsePacketCode.Incomplete
                    Else
                        ret = ParsePacketCode.NoData
                    End If
                Else
                    ret = ParsePacketCode.NoData
                End If
            End Try

            If ret = EvoLib.Protocol.ParsePacketCode.NoData Then
                Exit Sub
            ElseIf ret = EvoLib.Protocol.ParsePacketCode.PacketError Then
                If Globals.bDebugMode Then Debug.Print("MirEvo.mSocket.gSock_onDataRecieved: ParsePacket returned EvoLib.Protocol.PacketError" & vbCrLf)

            ElseIf ret = EvoLib.Protocol.ParsePacketCode.Incomplete Then
                If Globals.bDebugMode Then Debug.Print("MirEvo.mSocket.gSock_onDataRecieved: ParsePacket returned EvoLib.Protocol.Incomplete" & vbCrLf)
                If Not bRecievedBytes Is Nothing Then
                    ReDim bIncompleteData(bRecievedBytes.Length - 1)
                    Array.Copy(bRecievedBytes, 0, bIncompleteData, 0, bRecievedBytes.Length)
                End If

            ElseIf ret = EvoLib.Protocol.ParsePacketCode.DecryptError Then

                'If Globals.gGameState = Globals.GameState.Auth Then
                MessageBoxes.AddMessageBox("LOGINFAIL", "Encryption/Decryption Faliure!", MessageBoxes.MsgBoxButtons.OK, _
                                            AddressOf MsgBoxResultCallback)
                Auth.txtPass.Text = ""
                MessageBoxes.RemoveStateNotice()
                Globals.Crypto = Cryptography.DefaultCrypto
                Globals.sUsername = Protocol.DefaultUsername
                Auth.bCanLogin = False
                'End If

            ElseIf ret = EvoLib.Protocol.ParsePacketCode.OK Then

                'START DEBUG OUTPUT CODE

                If Globals.bDebugMode Then

                    Dim txt As String = vbCrLf & "===[CLIENT SOCKET > DATA IN]====================================================" & vbCrLf
                    txt &= "     UNAME: " & info.Username & vbCrLf

                    Select Case Globals.gGameState
                        Case Globals.GameState.Auth
                            txt &= "    EvoLib.Protocol.Packet: " & CType(info.PacketType, EvoLib.Protocol.LoginPacketType).ToString & vbCrLf
                        Case Globals.GameState.SelChar
                            txt &= "    EvoLib.Protocol.Packet: " & CType(info.PacketType, EvoLib.Protocol.LoginPacketType).ToString & vbCrLf
                        Case Globals.GameState.Game
                            txt &= "    EvoLib.Protocol.Packet: " & CType(info.PacketType, EvoLib.Protocol.GamePacketType).ToString & vbCrLf
                    End Select

                    txt &= "    OPCODE: " & info.PacketOpCode.ToString & vbCrLf & vbCrLf

                    If Not struct Is Nothing Then
                        txt &= "STRUCT BYTES:" & vbCrLf

                        Dim tmp As String
                        Dim asciitxt As String = ""
                        Dim i As Integer
                        Dim c As Integer
                        Dim tmpbyte(0) As Byte

                        For i = 0 To struct.Length - 1

                            tmp = struct(i)
                            Do Until tmp.Length = 3
                                tmp = " " & tmp
                            Loop

                            If tmp = 0 Then
                                txt &= "  . "
                            Else
                                txt &= " " & tmp
                            End If

                            If struct(i) < 32 Then
                                asciitxt &= "."
                            Else
                                tmpbyte(0) = struct(i)
                                asciitxt &= System.Text.Encoding.ASCII.GetString(tmpbyte)
                            End If

                            c += 1
                            If c = 20 Then
                                c = 0
                                txt &= "  " & asciitxt & vbCrLf
                                asciitxt = ""
                            ElseIf c = 10 Then
                                asciitxt &= "   "
                                txt &= "    "
                            End If

                        Next

                        If c < 10 Then txt = txt & "    "
                        If c > 0 Then txt &= "  " & Space((20 - c) * 4) & asciitxt & vbCrLf

                        txt &= struct.Length & " bytes" & vbCrLf

                    End If

                    Debug.Print(txt)
                End If

                'STOP DEBUG OUTPUT CODE

                Select Case CType(info.PacketType, EvoLib.Protocol.GenericPacketType)

                    ''Maintainance mode code
                    'Case EvoLib.Protocol.GenericPacketType.ServerState
                    '    Select Case info.PacketOpCode
                    '        Case EvoLib.Protocol.PacketOpCode.NegAcknowledge
                    '            Dim msg As String = EvoLib.Protocol.Functions.ArrayToStruct(struct, GetType(EvoLib.Protocol.GenericMessage)).message

                    '            'MessageBoxes.RemoveStateNotice()
                    '            MessageBoxes.AddMessageBox("SERVERSTATE", msg, MessageBoxes.MsgBoxButtons.OK, _
                    '            AddressOf MsgBoxResultCallback)

                    '    End Select

                    Case EvoLib.Protocol.GenericPacketType.ClientVersion
                        Select Case info.PacketOpCode
                            '        Case EvoLib.Protocol.PacketOpCode.Enquiry
                            '            'server is requesting the client version string
                            '            Dim reply As EvoLib.Protocol.Packet
                            '            reply.Username = Globals.sUsername
                            '            reply.PacketType = EvoLib.Protocol.GenericPacketType.ClientVersion
                            '            reply.PacketOpCode = EvoLib.Protocol.PacketOpCode.Answer

                            '            Dim clientver As EvoLib.Protocol.ClientVersion
                            '            clientver.CLIENTVER = Protocol.sClientVersion

                            '            ClientSock.SendData(EvoLib.Protocol.MakePacket(reply, clientver, Nothing)) 'Globals.Crypto))

                            '            MessageBoxes.SetStateNotice("Validating client version...")

                            '        Case EvoLib.Protocol.PacketOpCode.Acknowledge
                            '            'server client version is matching
                            '            Dim reply As EvoLib.Protocol.Packet
                            '            reply.Username = Globals.sUsername
                            '            reply.PacketType = EvoLib.Protocol.GenericPacketType.Username

                            '            If Auth.bCanLogin Then

                            '                ' Transfering servers, init new Session on server using 
                            '                ' old SessionID

                            '                reply.PacketOpCode = EvoLib.Protocol.PacketOpCode.Notify
                            '                Dim sid As EvoLib.Protocol.Username
                            '                sid.Username = Globals.sUsername

                            '                ClientSock.SendData(EvoLib.Protocol.MakePacket(reply, sid))

                            '            Else

                            '                'Request a new Session

                            '                reply.PacketOpCode = EvoLib.Protocol.PacketOpCode.Enquiry
                            '                ClientSock.SendData(EvoLib.Protocol.MakePacket(reply))

                            '                MessageBoxes.SetStateNotice("Starting session...")

                            '            End If

                            Case EvoLib.Protocol.PacketOpCode.NegAcknowledge
                                'server client version does not match
                                MessageBoxes.RemoveStateNotice()
                                MessageBoxes.AddMessageBox("CLIENTVERERROR", _
                                "The client version does not match the server, please obtain the correct version for this server!", _
                                MessageBoxes.MsgBoxButtons.OK, AddressOf MsgBoxResultCallback)

                        End Select

                        'Case EvoLib.Protocol.GenericPacketType.Username
                        '    Select Case info.PacketOpCode

                        '        Case EvoLib.Protocol.PacketOpCode.Answer
                        '            'server is assigning a SessionID
                        '            Dim sid As EvoLib.Protocol.Username = EvoLib.Protocol.Functions.ArrayToStruct(struct, GetType(EvoLib.Protocol.Username))
                        '            Array.Copy(sid.Username, Globals.sUsername, 4)
                        '            Globals.Crypto = New EvoLib.Cryptography.Crypto(EvoLib.Cryptography.Functions.UsernameToKey(sid.Username))
                        '            Auth.bCanLogin = True
                        '            MessageBoxes.RemoveStateNotice()

                        '            System.Windows.Forms.Application.DoEvents()

                        '            If Not Auth.HasAutoLogged And Auth.AutoLoginID.STR_USERNAME.Length > 0 Then

                        '                Auth.HasAutoLogged = True
                        '                Auth.TryLogin(True)

                        '            End If

                        '    End Select

                    Case EvoLib.Protocol.GenericPacketType.PingTest
                        Select Case info.PacketOpCode
                            Case EvoLib.Protocol.PacketOpCode.Acknowledge
                                Engine.Pong()

                        End Select

                    Case Else

                        Select Case Globals.gGameState

                            Case Globals.GameState.Auth
                                Auth.PacketArrival(info, struct)
                            Case Globals.GameState.SelChar
                                SelectChar.PacketArrival(info, struct)
                            Case Globals.GameState.Game
                                Game.PacketArrival(info, struct)
                            Case Globals.GameState.ServerSwitch
                                ServerSwitcher.PacketArrival(info, struct)

                        End Select

                        System.Windows.Forms.Application.DoEvents()

                        If Not bRecievedBytes Is Nothing Then
                            If bRecievedBytes.Length > 0 Then

                                ReDim bIncompleteData(bRecievedBytes.Length - 1)
                                Array.Copy(bRecievedBytes, 0, bIncompleteData, 0, bRecievedBytes.Length)
                                ReDim bRecievedBytes(0)
                                Call gSock_onDataRecieved(bRecievedBytes, 0)

                            End If
                        End If

                End Select

            Else
                If Globals.bDebugMode Then Debug.Print("API.Protocol.ParsePacket returned an invalid value!" & vbCrLf)
            End If

        End Sub

        Private Sub gSock_onDisconnect() Handles gSock.onDisconnect
            If gIsConnecting Then Exit Sub
            MessageBoxes.RemoveStateNotice()
            MessageBoxes.AddMessageBox("DISCONNECT", "Disconnected from server!", MessageBoxes.MsgBoxButtons.OK, AddressOf MsgBoxResultCallback)
        End Sub

        Public Sub MsgBoxResultCallback(ByRef result As MessageBoxes.MsgBoxResult)

            Select Case result.mName

                Case Is = "SERVERSTATE"
                    'Globals.bEnding = True

                Case Is = "CLIENTVERERROR"
                    Globals.bEnding = True

                Case Is = "DISCONNECT"
                    Globals.bEnding = True

            End Select

        End Sub

        Private Sub gSock_onError(ByRef sDescription As String, ByRef iNumber As Integer) Handles gSock.onError

            Debug.WriteLine("Sock Error " & iNumber & " : " & sDescription)

            MessageBoxes.RemoveStateNotice()
            MessageBoxes.AddMessageBox("DISCONNECT", "Sock Error " & iNumber & " : " & sDescription, MessageBoxes.MsgBoxButtons.OK, AddressOf MsgBoxResultCallback)

        End Sub

    End Module

End Namespace
